01 Docker基础入门
在应用开发和部署过程中,环境差异是最常见的问题之一。开发环境中运行正常的AI Agent服务,迁移到其他机器后可能会因为Python版本、系统依赖或环境变量不同而无法启动。
Docker的思路是把应用及其运行所需的环境一起打包成镜像。目标机器安装Docker后,即可按照同一套运行环境启动应用。
一、Docker是什么
Docker是一个开源的应用容器化平台。它最核心的能力可以概括成一句话:把应用和运行环境打包在一起,实现一次构建、到处运行。
传统部署通常需要在服务器上安装Python、安装依赖、配置环境变量并上传代码,任何一个环节不一致都可能导致运行失败。Docker则把代码、Python运行时、依赖库和配置文件放进同一个"容器"里,使应用在不同机器上保持一致的运行方式。
1.1 容器和虚拟机的区别
容器和虚拟机经常被放在一起比较,但它们不是一类东西。
| 对比项 | 虚拟机 | 容器 |
|---|---|---|
| 隔离级别 | 完整的操作系统内核 | 进程级隔离 |
| 启动速度 | 分钟级 | 秒级 |
| 资源占用 | 大(每个VM独立内核) | 小(共享宿主机内核) |
| 镜像大小 | GB级 | MB级 |
| 适合场景 | 需要完整OS隔离 | 应用级别的隔离和部署 |
虚拟机提供完整的操作系统隔离,容器则提供更轻量的应用级隔离。容器更像标准化的运行单元,便于快速迁移和部署。
1.2 Docker的核心价值
Docker最实用的价值,主要落在这三件事上:
- 环境一致性——开发、测试、生产环境尽量保持一致,减少环境差异导致的问题
- 快速部署——镜像打包一次,启动通常是秒级的,不用在每台机器上重新安装依赖
- 资源高效——容器共享宿主机的操作系统内核,比虚拟机轻得多,同一台服务器能跑更多应用
二、Docker架构
Docker采用客户端-服务端架构。终端中的docker命令负责发送指令,后台的Docker Daemon负责执行具体操作。
┌──────────────────┐
│ Docker Client │ ← docker 命令
│ (docker CLI) │
└────────┬─────────┘
│ REST API
▼
┌──────────────────┐
│ Docker Daemon │ ← 后台服务,干脏活累活
│ (dockerd) │
└────────┬─────────┘
│
┌────┴────┐
▼ ▼
镜像 容器
(Images) (Containers)- Docker Client:也就是终端中的
docker命令,负责把指令发给Docker Daemon - Docker Daemon:常驻后台的守护进程,负责构建镜像、运行容器、管理网络和存储
- Docker Registry:用来存储和分发镜像的服务,最常见的公共仓库是Docker Hub
Client和Daemon可以在同一台机器上,也可以隔着网络连接到远程Daemon。
三、容器
容器是镜像跑起来之后的实例。一个镜像可以启动多个容器,这些容器彼此隔离,互不干扰。
理解容器时,可以抓住四个特点:
- 自包含——运行所需的内容都在容器里,不指望宿主机提前装好一堆东西
- 隔离——容器之间互不影响,一个容器异常退出不会影响其他容器
- 独立——每个容器都可以单独管理,删除一个容器不会影响其他容器
- 可移植——在开发机器上能运行的容器,迁移到Linux服务器后也能运行
3.1 第一个容器
装好Docker Desktop之后,可以先跑一个最小的例子:
docker run -d -p 8080:80 docker/welcome-to-docker这条命令会执行以下操作:
- 从Docker Hub拉取
docker/welcome-to-docker镜像 - 创建一个新容器
- 把容器的80端口映射到宿主机的8080端口
- 在后台运行(
-d)
打开浏览器访问http://localhost:8080,可以看到欢迎页面。
3.2 常用容器命令
# 查看正在运行的容器
docker ps
# 查看所有容器(包括已停止的)
docker ps -a
# 停止容器
docker stop <容器ID>
# 启动已停止的容器
docker start <容器ID>
# 删除容器
docker rm <容器ID>
# 进入容器内部
docker exec -it <容器ID> /bin/bashdocker ps的输出里会列出容器ID、使用的镜像、运行状态、端口映射等信息。操作容器时,通常只需要使用容器ID的前几位,不必复制完整ID。
四、镜像
镜像是一个只读模板,包含运行容器需要的所有文件和配置。可以把镜像理解成一张"安装光盘",容器就是从这张光盘启动出来的"系统"。
镜像有两个重要特点:
- 不可变——镜像一旦创建就不能修改,只能基于它创建新镜像
- 分层存储——镜像由多个层(layer)组成,每一层代表一组文件系统变更
4.1 镜像的分层结构
一个典型的Python应用镜像可能包含以下层:
第5层:复制应用源代码 (几KB)
第4层:安装pip依赖 (几十MB)
第3层:复制requirements.txt (几百字节)
第2层:安装Python运行时 (几百MB)
第1层:基础Linux系统 (几十MB)分层的好处是可以复用。比如两个Python应用都基于同样的基础系统和Python运行时,它们就能共享前两层,不需要重复存储。只修改应用代码时,通常只需要重建最上面的几层,不需要从头构建整个镜像。
4.2 镜像操作命令
# 拉取镜像
docker pull python:3.12-alpine
# 查看本地镜像列表
docker images
# 查看镜像的层信息
docker image history <镜像名>
# 删除镜像
docker rmi <镜像名>
# 给镜像打标签
docker tag <镜像名> <新标签>4.3 镜像标签
镜像用标签区分版本。以python:3.12-alpine为例,python是镜像名,3.12-alpine是标签。如果不写标签,Docker默认使用latest。
注意:生产环境不要用latest标签。 latest指向的版本可能会变,今天构建用到的latest和三个月后的latest未必是同一个东西。明确写出版本号,构建结果才更可控。
五、仓库
Docker Registry(仓库)负责存储和分发镜像。Docker Hub是最常用的公共仓库,可以类比成镜像世界里的GitHub。
5.1 Docker Hub
Docker Hub上有三类镜像:
| 类型 | 说明 | 示例 |
|---|---|---|
| Docker Official Images | 官方维护,安全可靠 | python、nginx、redis |
| Docker Verified Publishers | 经过Docker认证的商业镜像 | datadog、gitlab |
| 社区镜像 | 任何人可以发布 | 各种第三方镜像 |
生产环境优先使用官方镜像或Verified Publisher镜像。社区镜像不是不能用,但一定要认真审查来源和内容。
5.2 推送镜像到仓库
# 登录Docker Hub
docker login
# 给镜像打标签(格式:用户名/镜像名:标签)
docker tag my-app:1.0 yourusername/my-app:1.0
# 推送镜像
docker push yourusername/my-app:1.0团队内部也可以搭建私有仓库来存储内部镜像,本文不展开说明。
六、总结
学Docker,先把这四个概念放稳:
| 概念 | 是什么 | 类比 |
|---|---|---|
| 容器(Container) | 镜像的运行实例 | 运行中的程序 |
| 镜像(Image) | 只读的模板 | 安装光盘 |
| 仓库(Registry) | 存储镜像的服务 | 应用商店 |
| Dockerfile | 构建镜像的脚本 | 安装说明书(下篇讲) |
最常用的命令:
| 命令 | 作用 |
|---|---|
docker run | 创建并启动容器 |
docker ps | 查看运行中的容器 |
docker images | 查看本地镜像 |
docker pull | 拉取镜像 |
docker stop | 停止容器 |
docker rm | 删除容器 |
这些概念和命令是后续内容的基础。Dockerfile用于通过文本文件定义镜像构建过程,是进一步使用Docker的重要内容。